import asyncore
import asynchat


class EndSession(Exception):
    pass


class ChatServer(asyncore.dispatcher):  # 定义聊天服务器类

    def __init__(self, port):  # 重写构造方法
        asyncore.dispatcher.__init__(self)  # 重载超类的构造方法
        self.create_socket()  # 创建套接字对象
        self.set_reuse_addr()  # 设置地址可重用
        self.bind(('localhost', port))  # 绑定本机地址与端口
        self.listen(5)  # 设置监听连接数
        self.users = {}  # 记录当前用户的一个字典
        self.main_room = ChatRoom(self)

    def handle_accepted(self, sock, addr):  # 重写处理客户端连接的方法
        print('当前连接', sock, addr)
        ChatSession(self, sock)


class ChatSession(asynchat.async_chat):
    """
    负责和客户端通信
    """
    def __init__(self, server, sock):
        asynchat.async_chat.__init__(self, sock)
        self.server = server
        self.set_terminator(b'\n')  # 设置聊天数据分隔协议，以换行符作为数据结束的表示
        self.data = []  # 创建数据列表
        self.name = None  # 保存用户名字
        self.enter(LoginRoom(self.server))

    def enter(self, room):
        # 从当前房间移除自身，然后添加到指定房间
        try:
            cur = self.room
        except AttributeError:
            pass
        else:
            cur.remove(self)
        self.room = room
        room.add(self)

    def collect_incoming_data(self, data):  # 重写处理客户端发来数据的方法
        self.data.append(data.decode())  # 将客户端发来的数据添加到数据列表

    def found_terminator(self):
        """
        当客户端的一条数据结束时调用
        :return:
        """
        line = ''.join(self.data)  # 将消息记录拼接成字符串
        self.data = []  # 清空数据列表
        try:
            self.room.handle(self, line.encode())
        except EndSession:
            self.handle_close()

    def handle_close(self):
        asynchat.async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))


class CommandHandler:

    def handle(self, session, line):
        line = line.decode()
        #  处理命令
        if not line.strip():
            return

        parts = line.split(' ', 1)  # 以空格为分隔符，分隔成两个
        # print('----------------')
        # print(line)
        # print('----------------')
        cmd = parts[0]  # do_login msg
        try:
            line = parts[1].strip()
        except IndexError:
            line = ''
        #  通过代码执行对应的方法
        method = getattr(self, 'do_' + cmd, None)
        try:
            method(session, line)
        except TypeError:
            pass


class Room(CommandHandler):
    """
    包含多个用户环境，负责基本命令处理和广播消息
    """
    def __init__(self, server):
        self.server = server
        self.sessions = []  # 记录在线人数

    def add(self, session):
        self.sessions.append(session)

    def remove(self, session):
        self.sessions.remove(session)

    def broadcast(self, line):  # 定义广播的方法
        for session in self.sessions:  # 遍历所有会话
            session.push(line)  # 向所有会话的客户端推送内容

    def do_logout(self, session, line):
        raise EndSession


class ChatRoom(Room):
    """
    代表正在聊天的用户
    """
    def add(self, session):
        session.push(b'login success')  # 发送给客户端
        self.broadcast((session.name + ' has entered the room. \n').encode())
        self.server.users[session.name] = session
        Room.add(self, session)

    def remove(self, session):
        Room.remove(self, session)
        self.broadcast((session.name + ' has left the room.\n').encode())

    def do_say(self, session, line):
        self.broadcast((session.name + ':' + line + '\n').encode())

    def do_look(self, session, line):
        """
        查询在线用户
        :param session:
        :param line:
        :return:
        """
        session.push(b'Online Users:\n')
        for user in self.sessions:
            session.push((user.name + '\n').encode())


class LoginRoom(Room):

    def add(self, session):  # session就是传入的self.server
        # 用户连接成功返回数据
        Room.add(self, session)
        session.push(b'Connect Success')

    def do_login(self, session, line):
        name = line.strip()
        if not name:
            session.push(b'Username empty')
        elif name in self.server.users:
            session.push(b'Username exists')
        else:
            session.name = name
            session.enter(self.server.main_room)


class LogoutRoom(Room):
    """
    处理退出用户
    """
    def add(self, session):
        #  从服务器中移除
        try:
            del self.server.users[session.name]
            print('用户{}离线了'.format(session.name))
        except KeyError:
            print('User does not exists')


if __name__ == '__main__':
    PORT = 6666 # 设置服务器端口号
    ChatServer(PORT)  # 实例化聊天服务器
    print('服务器在0.0.0.0：{}开始运行'.format(PORT))
    asyncore.loop()  # 运行异步循环
